
#ifndef RTCONNECTIONINTERFACE_H
#define RTCONNECTIONINTERFACE_H

#include "RtDefines.h"
#include "RtReferenceControl.h"
#include "RtUtilTemplates.h"
#include "RtMutex.h"

#ifdef RUDP_ENABLE_STATISTICS
#include "RtRudpStatistics.h"
#endif

class CRtInetAddr;
class CRtTimeValue;
class CRtMessageBlock;

class IRtAcceptorConnectorSink;
class IRtTransportSink;
class IRtReferenceControl;
class IRtTransport;
class IRtAcceptorConnectorId;
class IRtConnector;
class IRtDetectionConnector;
class IRtAcceptor;

#define RUDP_TTL_FOR_AUDIO 0 /// Resending times.

class RT_API_EXPORT CRtConnectionManager
{
public:
	static CRtConnectionManager* Instance();
	static void CleanupInstance();

	typedef DWORD CType;
	enum { 
		CTYPE_NONE = 0,
		CTYPE_TCP = (1 << 0),
		CTYPE_UDP = (1 << 1),
		CTYPE_SSL_DIRECT = (1 << 2),
		CTYPE_SSL_WITH_BROWER_PROXY = (1 << 3),
		CTYPE_SSL = CTYPE_SSL_DIRECT | CTYPE_SSL_WITH_BROWER_PROXY,
		CTYPE_HTTP_DIRECT = (1 << 4),
		CTYPE_HTTP_WITH_BROWER_PROXY = (1 << 5),
		CTYPE_HTTP = CTYPE_HTTP_DIRECT | CTYPE_HTTP_WITH_BROWER_PROXY,
		CTYPE_HTTPS_DIRECT = (1 << 6),
		CTYPE_HTTPS_WITH_BROWER_PROXY = (1 << 7),
		CTYPE_HTTPS = CTYPE_HTTPS_DIRECT | CTYPE_HTTPS_WITH_BROWER_PROXY,
		CTYPE_TCP_WITH_BROWER_PROXY = (1 << 15),

		/// the range of connection type is from (1 << 0) to (1 << 15)
		CTYPE_TYPE_MASK = 0xFFFF,

		CTYPE_PDU_LENGTH = (1 << 16),
		CTYPE_PDU_PACKAGE = (1 << 17),
		CTYPE_PDU_KEEPALIVE = (1 << 18),
		CTYPE_PDU_RECONNECT = (1 << 19),
		CTYPE_PDU_ACK = (1 << 20),

		CTYPE_PDU_RELIABLE = CTYPE_PDU_PACKAGE | CTYPE_PDU_KEEPALIVE | CTYPE_PDU_RECONNECT | CTYPE_PDU_ACK,
		CTYPE_PDU_ANY = CTYPE_PDU_RELIABLE,

		CTYPE_PACKAGE_TCP            = CTYPE_TCP | CTYPE_PDU_PACKAGE | CTYPE_PDU_KEEPALIVE,
		CTYPE_RELIABLE_TCP           = CTYPE_TCP | CTYPE_PDU_RELIABLE,
		CTYPE_PACKAGE_TCP_VIA_PROXY  = CTYPE_TCP_WITH_BROWER_PROXY | CTYPE_PDU_PACKAGE | CTYPE_PDU_KEEPALIVE,
		CTYPE_RELIABLE_TCP_VIA_PROXY = CTYPE_TCP_WITH_BROWER_PROXY | CTYPE_PDU_RELIABLE,
		CTYPE_RUDP                   = CTYPE_UDP | CTYPE_PDU_PACKAGE | CTYPE_PDU_ACK,

		/// the range of PDU is from (1 << 16) to (1 << 27)
		CTYPE_PDU_MASK = 0xFFF0000,

		/// property of sending.
		CTYPE_SEND_NO_PARTIAL_DATA = (1 << 28),
		CTYPE_SEND_PRIORITY = (1 << 29),

		/// the range of sending is from (1 << 28) to (1 << 31)
		CTYPE_SEND_MASK = 0xF0000000,
	};

	/// Create <IRtConnector>.
	RtResult CreateConnectionClient(
		CType aType, 
		IRtConnector *&aConClient);

	/// Create <IRtDetectionConnector>.
	RtResult CreateDetectionConnectionClient(
		IRtDetectionConnector *&aConClient);

	/// Create <IRtAcceptor>.
	RtResult CreateConnectionServer(
		CType aType, 
		IRtAcceptor *&aAcceptor);

public:
	~CRtConnectionManager();

private:
	CRtConnectionManager();

	/**RtResult SpawnNetworkThread_i();**/

	RtResult CreateConnectionClient_i(
		CType aType, 
		IRtConnector *&aConClient);

	RtResult CreateConnectionServer_i(
		CType aType, 
		IRtAcceptor *&aAcceptor);

	//For Creating Connection Service Connector
	RtResult CreateCnConnectionClient(
		CType &aType, 
		IRtConnector *&aConClient);

	//For Creating Connection Service Acceptor
	RtResult CreateCnConnectionServer(
		CType &aType, 
		IRtAcceptor *&aAcceptor);

	static CRtConnectionManager s_rtConnectionManagerSingleton;
};

class CRtTransportParameter
{
public:
	CRtTransportParameter()
	{
	}
};

/// the sink classes don't need ReferenceControl
class IRtAcceptorConnectorSink 
{
public:
	/**
	 * <aReason> indicates Connect successful or failed, 
	 * if successful, <aTrpt> is not NULL, otherwise is NULL.
	 * <aRequestId> indicates which Connector calls it.
	 */
	virtual void OnConnectIndication(
		RtResult aReason,
		IRtTransport *aTrpt,
		IRtAcceptorConnectorId *aRequestId) = 0;

protected:
	virtual ~IRtAcceptorConnectorSink() {}
};

class IRtTransportSink 
{
public:
	virtual void OnReceive(
		CRtMessageBlock &aData,
		IRtTransport *aTrptId,
		CRtTransportParameter *aPara = NULL) = 0;

	virtual void OnSend(
		IRtTransport *aTrptId,
		CRtTransportParameter *aPara = NULL) = 0;

	virtual void OnDisconnect(
		RtResult aReason,
		IRtTransport *aTrptId) = 0;

protected:
	virtual ~IRtTransportSink() {}
};

/// Reliable UDP Data Packet Reliability Definition. 
typedef enum {
	RUDP_UNRELIABLE, // Same as regular UDP, except that it will also discard duplicate packets. 
	RUDP_UNRELIABLE_SEQUENCED, // Regular UDP with a sequence counter.  Out of order packets will be discarded. 
	RUDP_RELIABLE, // The packet is sent reliably, but not necessarily in any order. 
	RUDP_RELIABLE_SEQUENCED, // The packet is reliable and will arrive in the sequence you sent it.  Out of order packets will be dropped. 
	RUDP_RELIABLE_ORDERED, // The packet is reliable and will arrive in the order you sent it. Packets will be delayed while waiting for out of order packets. 
} RtRudpReliability;

class IRtTransport : public IRtReferenceControl
{
public:
	typedef enum {
		DP_SYSTEM, 
		DP_HIGH, 
		DP_MEDIUM,  
		DP_LOW,
		DP_NUMBER_OF_PRIORITIES
	} DataPriority;
	
	typedef BYTE RudpChannel;

	virtual RtResult OpenWithSink(IRtTransportSink *aSink) = 0;

	virtual IRtTransportSink* GetSink() = 0;

	/***********************************************************************************
	 * Function: SendData()
	 * Description: 
	 *   Send data to peer of the connection. 
	 * Parameters:
	 * 	 inData: 
	 *     input data to be sent;
	 *   inSendImmediately: 
	 *     input indicates whether data should be sent out immediately. 
	 *     Make sense only for Reliable TCP connection. If FALSE, data will be cached 
	 * 	   in the sending buffer for at most 500 milliseconds.
	 * 	 inDestroyData: 
	 *     input indicates whether data should be destroyed by the network module. 
	 *     If TRUE, [inData] must point to a CRtMessageBlock data created by 
	 *     CRtMessageBlock::DuplicateChained(), and then network module will be responsible of 
	 *     destroying [inData] (via CRtMessageBlock::DestroyChained() ) when SendData() 
	 *     returns RT_OK, otherwise, the network module will do nothing about [inData].
	 * 	 inPriority: 
	 *     input data priority;
	 * 	 inNeedAck: 
	 *     input indicates whether data should be acknowledged from peer. Make sense 
	 * 	   only for Reliable TCP;
	 * Return: 
	 * 	 RT_OK - successfully;
	 * 	 RT_ERROR_PARTIAL_DATA - data is not sent out or is only sent out partially due to 
	 *     socket buffer or transport sending buffer is exhausted, thus upper layer should 
	 *     wait for OnSend() callback before trying sending data again;
	 * 	 RT_ERROR_NOT_AVAILABLE - invalid connection.
	 ***********************************************************************************/
	virtual RtResult SendData(
		CRtMessageBlock& inData,
		BOOL inSendImmediately = TRUE,
		BOOL inDestroyData = FALSE,
		DataPriority inPriority = DP_MEDIUM, 
		BOOL inNeedAck = TRUE) = 0;

	/***********************************************************************************
	 * Function: SendData()
	 * Description: 
	 *   Another version of SendData(), it's originally provided for reliable UDP 
	 *   connections. For other kinds of connection, calling this function is just equivalent 
	 *   to SendData(inData, inPriority, FALSE, inDestroyData);
	 * Parameters:
	 * 	 inData: 
	 *     input data to be sent;
	 * 	 inReliability: 
	 *     input data reliability;
	 * 	 inTTL: 
	 *     input time-to-live value of data, that is, resending times of data. 
	 *     Makes sense only for RUDP reliable data. If inTTL value is -1, data will 
	 *     be resent until its ACK is received, otherwise, data will be resent until 
	 *     whether its ACK is received or TTL tick expires.
	 *   inSendImmediately: 
	 *     input indicates whether data should be sent out immediately. 
	 *     Makes sense only for Reliable TCP connection. If FALSE, data will be cached 
	 * 	   in the sending buffer for at most 500 milliseconds.
	 * 	 inDestroyData: 
	 *     input indicates whether data should be destroyed by the network module. 
	 *     If TRUE, [inData] must point to a CRtMessageBlock data created by 
	 *     CRtMessageBlock::DuplicateChained(), and then network module will be responsible of 
	 *     destroying [inData] (via CRtMessageBlock::DestroyChained() ) when SendData() 
	 *     returns RT_OK, otherwise, the network module will do nothing about [inData].
	 * 	 inPriority: 
	 *     input data priority;
	 * 	 inChannelID: 
	 *     input RUDP channel ID, ranging from 0 to 31, only makes sense for sequenced 
	 *     or ordered data; there are 32 channels for sequenced data transmitting, and data 
	 *     in the same channel are sequence-correlative; similarly, there are 32 channels for 
	 *     ordered data transmitting, and data in the same channel are orderring-correlative; 
	 * Return: 
	 * 	 RT_OK - successfully;
	 * 	 RT_ERROR_PARTIAL_DATA - data is not sent out or is only sent out partially due to 
	 *     socket buffer or transport sending buffer is exhausted, thus upper layer should 
	 *     wait for OnSend() callback before trying sending data again;
	 * 	 RT_ERROR_NOT_AVAILABLE - invalid connection.
	 ***********************************************************************************/
	virtual RtResult SendData(
		CRtMessageBlock& inData, 
		RtRudpReliability inReliability,
		INT inTTL = -1,
		BOOL inSendImmediately = TRUE,
		BOOL inDestroyData = FALSE,
		DataPriority inPriority = DP_MEDIUM,
		RudpChannel inChannelID = 0);

	/// the <aCommand>s are all listed in file RtErrorNetwork.h
	virtual RtResult SetOption(DWORD aCommand, LPVOID aArg) = 0;
	virtual RtResult GetOption(DWORD aCommand, LPVOID aArg) = 0;

	/// Disconnect the connection, and will not callback <IRtTransportSink> longer.
	virtual RtResult Disconnect(RtResult aReason) = 0;

protected:
	virtual ~IRtTransport() {}
};

class IRtAcceptorConnectorId : public IRtReferenceControl
{
public:
	virtual BOOL IsConnector() = 0;
	
protected:
	virtual ~IRtAcceptorConnectorId() {}
};

class IRtConnector : public IRtAcceptorConnectorId
{
public:
	/**
	 * If <aTimeout> equals NULL, there is no limit time for connecting.
	 * No return value, IRtAcceptorConnectorSink::OnConnectIndication() will 
	 * callback to indicate successful or failed.
	 * 
	 * <aAddrLocal> is bind() before connect() if it is not NULL.
	 *
	 * If you want to send some user data in the connect request,
	 * call SetOption(RT_OPT_CONNECTION_CONNCET_DATA) before call this funtion.
	 */
	virtual void AsycConnect(
		IRtAcceptorConnectorSink *aSink,
		const CRtInetAddr &aAddrPeer, 
		CRtTimeValue *aTimeout = NULL,
		CRtInetAddr *aAddrLocal = NULL) = 0;

	/**
	 * IRtAcceptorConnectorSink::OnConnectIndication() will never callback 
	 * after invoking it.
	 */
	virtual void CancelConnect() = 0;

protected:
	virtual ~IRtConnector() {}
};

class IRtDetectionConnector : public IRtConnector
{
public:
	enum DetectMode {
		DETECT_CONCURRENT = 0,
		DETECT_INTERVAL_1S,
	};
	/// add connection one by one, and the prior has high priority.
	virtual RtResult AddConnection(
		CRtConnectionManager::CType Type, 
		const CRtInetAddr &aAddrPeer,
		CRtTimeValue *aTimeDelay) = 0;

	/// Start connecting at the same time in <aTimeout>,
	/// If low priority connection connected, wait high priority connection in <aTimeDelay>.
	virtual void StartDetectionConnect(
		IRtAcceptorConnectorSink *aSink,
		CRtTimeValue *aTimeout = NULL,
		DetectMode inMode = DETECT_CONCURRENT,
		CRtInetAddr *aAddrLocal = NULL) =0;

protected:
	virtual ~IRtDetectionConnector() {}
};

class IRtAcceptor : public IRtAcceptorConnectorId
{
public:
	virtual RtResult StartListen(
		IRtAcceptorConnectorSink *aSink,
		const CRtInetAddr &aAddrListen) = 0;

	virtual RtResult StopListen(RtResult aReason) = 0;

protected:
	virtual ~IRtAcceptor() {}
};

/**
 * Default implementation of the second version of IRtTransport::SendData() for connections of 
 * other than reliable UDP.
 */
inline RtResult IRtTransport::SendData(
		CRtMessageBlock& inData, 
		RtRudpReliability inReliability,
		INT inTTL,
		BOOL inSendImmediately,
		BOOL inDestroyData,
		DataPriority inPriority,
		RudpChannel inChannelID)
{
	return SendData(inData, inSendImmediately, inDestroyData, inPriority);
}

#endif // RTCONNECTIONINTERFACE_H

